package com.cloud.async.business.context.support;

import com.cloud.async.business.context.AbstractApplicationContext;
import com.cloud.async.business.context.data.SingleDataSource;
import com.cloud.async.result.core.factory.AsyncResultFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

/**
 * @author chengrui
 * @title: ApplicationAsyncContext
 * @projectName webCloudAsync
 * @description: 完整子类，继承体系中最底层的实现
 * @date 2021/9/23 23:58
 */

public class StandardAsyncContext extends AbstractApplicationContext {

    /**
     * logger
     */
    private final Log logger = LogFactory.getLog(getClass());

    /**
     * 数据源
     */
    private ConcurrentHashMap<String, CompletableFuture<String>> data;


    /**
     * 初始化本地存储Map
     * @param dataCapacity
     * @param dataLoadFactor
     * @param dataConcurrencyLevel
     */
    private void initializeDataSource(Integer dataCapacity, Float dataLoadFactor, Integer dataConcurrencyLevel) {
        if (dataCapacity != null && dataLoadFactor != null && dataConcurrencyLevel != null) {
            this.data = SingleDataSource.INSTANCE.getSingleDataSource(dataCapacity, dataLoadFactor, dataConcurrencyLevel);
            return;
        }
        if (dataCapacity != null && dataLoadFactor != null) {
            this.data = SingleDataSource.INSTANCE.getSingleDataSource(dataCapacity, dataLoadFactor);
            return;
        }
        this.data = SingleDataSource.INSTANCE.getSingleDataSource(dataCapacity);
    }



    @Override
    protected void releaseData(String key) {
        this.data.remove(key);
    }

    @Override
    public String getResult(String key) {
        try {
            if (contains(key)) {
                CompletableFuture<String> function = this.data.get(key);
                return function.get(getMilliSecond(), TimeUnit.MILLISECONDS);
            }
        } catch (Exception e) {
            logger.warn("Failed to get data - " + "The exception information is: " + e);
        } finally {
            releaseData(key);
        }
        return null;
    }

    @Override
    public String getResult(String key, Long milliSecond) {
        try {
            if (contains(key)) {
                CompletableFuture<String> function = this.data.get(key);
                return function.get(milliSecond, TimeUnit.MILLISECONDS);
            }
        } catch (Exception e) {
            logger.error("Get result error - " + "The exception information is: " + e);
        } finally {
            releaseData(key);
        }
        return null;
    }

    @Override
    public boolean contains(String asyncKeyName) {
        return this.data.containsKey(asyncKeyName);
    }

    @Override
    public int getCount() {
        return this.data.size();
    }

    @Deprecated
    @Override
    public String[] getKeyNames() {
        return new String[0];
    }

    @Override
    protected Boolean saveData(String key, CompletableFuture value) {
        try {
            data.put(key, value);
            return true;
        } catch (Exception e) {
            logger.error("Failed to save data." + e);
            return false;
        }
    }

    /**
     * 基于有参构造进行创建对象
     * @param executor 线程池
     * @param asyncResultFactory 结果解析
     * @param milliSecond 全局任务等待时间
     * @param dataCapacity 当前数据源容器大小
     * @param dataLoadFactor 负载因子
     * @param dataConcurrencyLevel 并发级别
     */
    public StandardAsyncContext( Executor executor,
                                 AsyncResultFactory asyncResultFactory,
                                 Long milliSecond,
                                 Integer dataCapacity,
                                 Float dataLoadFactor,
                                 Integer dataConcurrencyLevel) {
        initializeExecutor(executor);
        initializeAsyncResultFactory(asyncResultFactory);
        initializeMilliSecond(milliSecond);
        initializeDataSource(dataCapacity, dataLoadFactor, dataConcurrencyLevel);
    }

}
